package com.itheima.project.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.project.adapter.FileStorageAdapter;
import com.itheima.project.constant.SuperConstant;
import com.itheima.project.constant.file.FileConstant;
import com.itheima.project.core.enums.file.FileEnum;
import com.itheima.project.exception.ExceptionCast;
import com.itheima.project.exception.ProjectException;
import com.itheima.project.mapper.FileMapper;
import com.itheima.project.pojo.File;
import com.itheima.project.service.IFileService;
import com.itheima.project.utils.BeanConv;
import com.itheima.project.utils.EmptyUtil;
import com.itheima.project.utils.EncodesUtil;
import com.itheima.project.utils.FileUrlContext;
import com.itheima.project.vo.file.FileVo;
import com.itheima.project.vo.file.UploadMultipartFile;
import io.seata.spring.annotation.GlobalTransactional;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @Description：附件 服务实现类
 */
@Service
public class FileServiceImpl extends ServiceImpl<FileMapper, File> implements IFileService {

    @Autowired
    IdentifierGenerator identifierGenerator;

    @Autowired
    FileStorageAdapter fileStorageAdapter;

    @Autowired
    private FileUrlContext fileUrlContext;

    @Value("${spring.cloud.alicloud.oss.bucket-name}")
    String bucketName;

    @Value("${spring.cloud.alicloud.oss.endpoint}")
    String endpoint;


    @Override
    public FileVo downLoad(Long fileId) throws ProjectException {
        File file = getById(fileId);
        String base64Image = null;
        try {
            InputStream inputStream = fileStorageAdapter.downloadFile(file.getStoreFlag(),
                    file.getBucketName(),
                    file.getPathUrl()
            );
            byte[] bytes = IOUtils.toByteArray(inputStream);
            base64Image = EncodesUtil.encodeBase64(bytes);

        }catch (Exception e){
            throw new ProjectException(FileEnum.UPLOAD_FAIL);
        }
        FileVo fileVo = BeanConv.toBean(file, FileVo.class);
        fileVo.setBase64Image(base64Image);
        return fileVo;
    }

    @Override
    public File upLoad(UploadMultipartFile multipartFile,
                        FileVo fileVo) throws ProjectException {
        String originalFilename = multipartFile.getOriginalFilename();
        String filename = identifierGenerator.nextId(fileVo)+"-"+originalFilename;
        String businessType = fileVo.getBusinessType().replace("/", "");

        File file = File.builder()
                .businessType(businessType)
                .fileName(filename)
                .suffix(originalFilename.substring(originalFilename.lastIndexOf(".")))
                .enterpriseId(fileVo.getEnterpriseId())
                .build();

        // 判断是否有指定的存储空间名称，如果没有使用系统默认的存储空间(aliyun)，如果有，使用指定的存储空间存储文件
        if (StringUtils.isEmpty(multipartFile.getBucketName())) {
            // 默认保存到aliyun的oss中，bucketname也为默认yml中的配置
            file.setBucketName(bucketName);
            file.setStoreFlag(FileConstant.ALIYUN_OSS);
        } else {
            // 使用指定的存储标识和指定的 bucketName 保存资源文件
            file.setBucketName(multipartFile.getBucketName());
            file.setStoreFlag(multipartFile.getStoreFlag());
        }

        // 如果不是自定生成文件目录，需要指定文件的存储文件地址
        if (!multipartFile.isAutoCatalog()) {
            file.setPathUrl(multipartFile.getStoreFilename());
        }

        String pathUrl = fileStorageAdapter.uploadFile(file,
                multipartFile.isAutoCatalog(),
                new ByteArrayInputStream(multipartFile.getFileByte()));

        // 如果是自动生成文件目录，需要赋值File对象中并将记录保存到数据库中
        if (multipartFile.isAutoCatalog()) {
            file.setPathUrl(pathUrl);
        }

        boolean result = save(file);
        if (!result) {
            ExceptionCast.cast(FileEnum.FILE_OPERATE);
        }

        // 获得前端可访问的完整资源路径地址
        String storeFlag = file.getStoreFlag();
        String fileUrl = fileUrlContext.getFileUrl(storeFlag, file.getPathUrl());
        file.setPathUrl(fileUrl);

        return file;
    }

    @Override
    public File bindBusinessId(FileVo fileVo) {
        File file = BeanConv.toBean(fileVo, File.class);
        boolean flag = updateById(file);
        if (flag){
            return file;
        }
        return null;
    }

    @Override
    public List<File> findFileVoByBusinessId(Long businessId) {
        QueryWrapper<File> queryWrapper = new QueryWrapper();
        queryWrapper.lambda().eq(File::getBusinessId,businessId);
        List<File> list = list(queryWrapper);
        if (!EmptyUtil.isNullOrEmpty(list)){
            list.forEach(n->{
                String fileUrl = fileUrlContext.getFileUrl(n.getStoreFlag(), n.getPathUrl());
                n.setPathUrl(fileUrl);
            });
        }
        return list;
    }

    @Override
    public Boolean deleteFileVoByBusinessId(Long businessId) {
        QueryWrapper<File> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(File::getBusinessId,businessId);
        List<File> files = list(queryWrapper);

        // 先删除本地数据，再删远端数据，方便数据回滚
        //删除数据库
        boolean result = this.remove(queryWrapper);

        //删除OSS中的图片
        if (!EmptyUtil.isNullOrEmpty(files)){
            List<String> getPathUrls = files.stream().map(File::getPathUrl).collect(Collectors.toList());
            File file = files.get(0);
            String bucketName = file.getBucketName();
            String storeFlag = file.getStoreFlag();
            fileStorageAdapter.deleteBatch(storeFlag,bucketName,getPathUrls);
        }
        return result;
    }

    @Override
    public Page<File> findFileVoPage(FileVo fileVo, int pageNum , int pageSize) {
        Page<File> page = new Page<>(pageNum,pageSize);
        QueryWrapper<File> queryWrapper = new QueryWrapper<>();

        if (!EmptyUtil.isNullOrEmpty(fileVo.getBusinessType())) {
            queryWrapper.lambda().eq(File::getBusinessType,fileVo.getBusinessType());
        }
        if (!EmptyUtil.isNullOrEmpty(fileVo.getPathUrl())) {
            queryWrapper.lambda().likeRight(File::getPathUrl,fileVo.getPathUrl());
        }
        if (!EmptyUtil.isNullOrEmpty(fileVo.getEnableFlag())) {
            queryWrapper.lambda().likeRight(File::getEnableFlag,fileVo.getEnableFlag());
        }
        queryWrapper.lambda().orderByDesc(File::getCreatedTime);
        page(page, queryWrapper);
        List<File> files = page.getRecords();
        if (!EmptyUtil.isNullOrEmpty(files)){
            files.forEach(n->{
                String fileUrl = fileUrlContext.getFileUrl(n.getStoreFlag(), n.getPathUrl());
                n.setPathUrl(fileUrl);
            });
        }
        page.setRecords(files);
        return page;
    }

    @Override
    @Transactional
    public Boolean deleteFile(String[] checkedIds) {
        for (String checkedId : checkedIds) {
            File file = getById(checkedId);
            //删除OSS中的图片
            if (!EmptyUtil.isNullOrEmpty(file)){
                fileStorageAdapter.delete(file.getStoreFlag(),file.getBucketName(),file.getPathUrl());
            }
        }
        //删除数据库
        return this.removeByIds(Arrays.asList(checkedIds));
    }

    @Override
    public Set<Long> findBusinessIdAll() {
        QueryWrapper<File> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(File::getEnableFlag, SuperConstant.YES).isNotNull(File::getBusinessId);
        List<File> list = list(queryWrapper);
        if (!EmptyUtil.isNullOrEmpty(list)){
            return list.stream().map(File::getBusinessId).collect(Collectors.toSet());
        }
        return null;
    }
}
